#pragma once

#include <map>

class CTimer
{
private:
  typedef std::map<UINT_PTR, void*>  TimerMap;
public:
  template <class T, VOID(T::*handler)(UINT, DWORD)>
  static UINT Start(void* pObj, DWORD nInterval)
  { 
    if (pObj == NULL)
    {
      return 0;
    }

    UINT nID = (int)::SetTimer(NULL, 0, nInterval, &CTimer::TimerProc<T, handler>);
    if (nID)
    {
      s_map.insert(std::make_pair<UINT_PTR, void*>(nID, pObj));
    }
    return nID;
  }

  static void Stop(UINT& nID)
  {
    if (nID)
    {
      ::KillTimer(NULL, nID);
      TimerMap::iterator iter = s_map.find(nID);
      if (iter != s_map.end())
        s_map.erase(iter);
      nID = 0;
    }
  }

private:
  template <class T, VOID(T::*handler)(UINT, DWORD)>
  inline static VOID CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
  {
    TimerMap::iterator iter = s_map.find(idEvent);
    if (iter != s_map.end())
    {
      (static_cast<T*>(iter->second)->*handler)(idEvent, dwTime);
    }
  }
  static TimerMap s_map;
};


template<class T, void(T::*handler)(UINT, DWORD)>
class CTimerInst
{
public:
  CTimerInst(void* pThis = NULL) : timerID(0), objPtr(pThis)
  {}
  ~CTimerInst()
  { if (timerID != 0) Stop(); }

  inline void Create(void* pThis)
  { objPtr = pThis; }

  inline bool Start(DWORD dwInterval)
  {
    if (timerID) CTimer::Stop(timerID);

    if (objPtr)
      timerID = CTimer::Start<T, handler>(objPtr, dwInterval);

    return timerID != 0;
  }

  void Stop()
  {
    if (timerID) CTimer::Stop(timerID);
  }
  inline bool IsActive()
  { return objPtr != NULL && timerID != 0; }

protected:
  UINT  timerID;
  void* objPtr;
};